#include "View.hpp"

namespace mi
{
    View::View(ViewOrientation orient) : 
        Widget(tView)
    {
        if (orient == None)
            orient = Vertical;
        orientation = orient;

        horSpacing = 1;
        verSpacing = 1;
        horPadding = 0;
        verPadding = 0;

        needToReorganize = false;
    }

    bool View::Contains(Widget* other)
    {
        if (other == this)
            return true;

        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            bool res = widgetList[i].widget->Contains(other);
            if (res)
                return true;
        }
        return false;
    }

    // Override input functions

    Widget* View::MouseClick(sf::Mouse::Button button, sf::MouseState state, int x, int y)
    {
        Widget* retWidget = NULL;
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            Widget *res = widgetList[i].widget->MouseClick(button, state, x, y);
            if (NULL != res)
                if (retWidget == NULL)
                    retWidget = res;
        }
        return retWidget;    
    }
    
    Widget* View::MouseMoved(int x, int y, int dx, int dy)
    {
        Widget* retWidget = NULL;
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            Widget *res = widgetList[i].widget->MouseMoved(x,y,dx,dy);
            if (NULL != res)
                if (retWidget == NULL)
                    retWidget = res;
        }
        return retWidget;
    }

    
    Widget* View::MouseDragged(int x, int y, int dx, int dy)
    {
        Widget* retWidget = NULL;
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            Widget *res = widgetList[i].widget->MouseDragged(x,y,dx,dy);
            if (NULL != res)
                if (retWidget == NULL)
                    retWidget = res;
        }
        return retWidget;
    }

    
    Widget* View::MouseWheel(int dir)
    {
        Widget* res;
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            res = widgetList[i].widget->MouseWheel(dir);
            if (NULL != res)
                return res;
        }
        return NULL;
    }

    
    Widget* View::KeysDown(sf::Event::KeyEvent key)
    {
        Widget* res;
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            res = widgetList[i].widget->KeysDown(key);
            if (NULL != res)
                return res;
        }
        return NULL;
    }

    
    Widget* View::KeysUp(sf::Event::KeyEvent key)
    {
        Widget* res;
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            res = widgetList[i].widget->KeysUp(key);
            if (NULL != res)
                return res;
        }
        return NULL;
    }

    Widget* View::TextEntered(char ch)
    {
        Widget* res;
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            res = widgetList[i].widget->TextEntered(ch);
            if (NULL != res)
                return res;
        }
        return NULL;
    }
    
    View& View::AddWidget (Widget *toAdd, int weight)
    {
        if (weight < 1)
            weight = 1;
        if (Contains(toAdd))
            return (*this);   // won't add same widget again
        widgetList.push_back(Member(toAdd, weight));
        needToReorganize = true;
        return (*this);
    }

    View& View::RemoveWidget (Widget *toRemove)
    {
        for(unsigned int i = 0; i < widgetList.size(); i++)
        {
            if (widgetList[i].widget->GetID().compare(ID) == 0)
            {
                widgetList.erase( widgetList.begin() + i );
                needToReorganize = true;
                return (*this);    // removed successfully
            }
        }
        return (*this);   // couldn't find widget to remove
    }

    Widget* View::FindWidget (std::string ID)
    {
        if (this->ID.compare(ID) == 0)
            return this;

        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            if (widgetList[i].widget->GetID().compare(ID) == 0)
                return widgetList[i].widget;
            if (widgetList[i].widget->GetType() == tView)
            {
                View *subView = (View*) widgetList[i].widget;
                Widget* res = subView->FindWidget(ID);
                if (res != NULL)
                    return res;
            }
        }
        return NULL;
    }

    ViewOrientation View::Orientation (ViewOrientation orient)
    {
        if (orient == None)
            return orientation;
        ViewOrientation oldOrientation = orientation;
        orientation = orient;
        needToReorganize = true;
        return oldOrientation;
    }

    // Overwrite the onMove and onResize functions

    void View::OnSizeChange(Vec2i oldSize)
    {
        needToReorganize = true;
    }

    void View::OnPositionChange(Vec2i oldPosition)
    {
        needToReorganize = true;
        return;
        Vec2i diff = oldPosition - position;
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            widgetList[i].widget->SetPos(widgetList[i].widget->GetPos() + diff);
        }
    }

    // Overwrite the pure virtuals from Widget

    void View::Draw2D ()
    {
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            widgetList[i].widget->Draw2D();
        }
    }

    bool View::Update (double dTime)
    {
        if (needToReorganize)
        {
            Reorganize();
            needToReorganize = false;
        }

        bool retval = false;
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            retval = retval || widgetList[i].widget->Update(dTime);
        }
        return retval;
    }

    // private

    void View::Reorganize ()
    {
        if (widgetList.size() == 0)
            return;     // don't waste my time!

        if (orientation == Horizontal)
            ReorganizeHorizontally();
        else if (orientation == Vertical)
            ReorganizeVertically();
        else
            LOG_INFO << "Reorganize call: Orientation was neither Horizontal nor Vertical!";
    }

    void View::ReorganizeHorizontally ()
    {
        // height of each component is fixed, as is the starting y position
        int height = size.y - 2 * verPadding;
        int startY = position.y + verPadding;

        // total combined width of the components = view width - padding on left and right - spacing between each component
        int totalAvailableWidth = size.x - 2*horPadding - (widgetList.size() - 1)*horSpacing;

        // the sum of the weight of each component
        int weightSum = GetWeightSum();

        // indicates the sum of the widths of the preciding components. To start with, there was no preceding, so its 0
        int usedUpWidth = 0;

        // the width of each component depends on its weight, as does the components starting x
        for (unsigned int i = 0; i < widgetList.size(); i++)
        {
            int startX = position.x + horPadding + i * horSpacing + usedUpWidth;            
            int width = (int) ( ( ( (double) widgetList[i].weight ) / ( (double) weightSum ) ) * totalAvailableWidth);
            usedUpWidth += width;

            widgetList[i].widget->SetSize(Vec2i(width, height));
            widgetList[i].widget->SetPos(Vec2i(startX, startY));
        }

    }

    void View::ReorganizeVertically ()
    {
        // width of each component is fixed
        int width = size.x - 2 * horPadding;

        // as is the starting x position
        int startX = position.x + horPadding;

        // total combined height of the components
        int totalAvailableHeight = size.y - 2*verPadding - (widgetList.size() - 1)*verSpacing;

        // the sum of the weight of all components
        int weightSum = GetWeightSum();

        // how much of the total height has been used up so far. At the start this is 0
        int usedUpHeight = 0;

        // store the list's size for use
        int listSize = (int)widgetList.size();

        // the height and starting y position of each component need to be calculated individually
        for (int i = listSize - 1; i >= 0; i--)
        {
            int startY = position.y + verPadding + (listSize - i - 1) * verSpacing + usedUpHeight;
            int height = (int) ( ( ( (double) widgetList[i].weight ) / ( (double) weightSum ) ) * totalAvailableHeight);
            usedUpHeight += height;

            widgetList[i].widget->SetSize(Vec2i(width, height));
            widgetList[i].widget->SetPos(Vec2i(startX, startY));
        }

    }

    int View::GetWeightSum ()
    {
        int sum = 0;
        for (unsigned int i = 0; i < widgetList.size(); i++)
            sum += widgetList[i].weight;
        return sum;
    }
}